/*
MIT License

Copyright (c) 2021 МГТУ им. Н.Э. Баумана, кафедра ИУ-6, Михаил Фетисов,

https://bmstu.codes/lsx/simodo/loom
*/

#include "simodo/parser/fuze/FuzeSblRdp.h"
#include "simodo/parser/fuze/BaseOperationCode.h"

#include "simodo/inout/format/fmt.h"

#include <cassert>

namespace simodo::parser
{

bool FuzeSblRdp::parse(inout::Tokenizer &tzer, inout::Token &t) {
    // "{" { <вызов> ";" } "}"
    //  ^
    ast().addNode_StepInto(SCRIPT_HOST_NAME, static_cast<ast::OperationCode>(BaseOperationCode::Block), t, t);
    ast().addNode(SCRIPT_HOST_NAME, static_cast<ast::OperationCode>(BaseOperationCode::Block), t, t);

    t = getToken(tzer);

    while(t.type() != inout::LexemeType::Empty) {
        if (t.type() == inout::LexemeType::Punctuation && t.lexeme() == u"}") {
            // "{" { <вызов> ";" } "}"
            //                      ^
            ast().addNode(SCRIPT_HOST_NAME, static_cast<ast::OperationCode>(BaseOperationCode::Block), t, t);
            /// \todo Обрабатывать возврат false!
            ast().goParent();

            t = getToken(tzer);
            break;
        }

        // "{" { <вызов> ";" } "}"
        //       ^
        if (!parseProcedureCalling(tzer,t))
            return false;

        // "{" { <вызов> ";" } "}"
        //                ^
        if (t.type() != inout::LexemeType::Punctuation || t.lexeme() != u";") {
            reportUnexpected(t, "';'");
            return false;
        }

        t = getToken(tzer);
    }

    return true;
}

bool FuzeSblRdp::parseProcedureCalling(inout::Tokenizer &tzer, inout::Token &t) {
    // <адрес>
    // ^
    if (!parseAddress(tzer,t))
        return false;

    ast().addNode(SCRIPT_HOST_NAME, static_cast<ast::OperationCode>(BaseOperationCode::Pop), t, t);

    return true;
}

bool FuzeSblRdp::parseAddress(inout::Tokenizer &tzer, inout::Token &t) {
    // <AddressAtom> {["." <AddressAtom>] | "(" [<список параметров>] ")"}
    // ^
    if (!parseAddressAtom(tzer,t))
        return false;

    while(t.type() != inout::LexemeType::Empty) {
        // <AddressAtom> {["." <AddressAtom>] | "(" [<список параметров>] ")"}
        //               ^
        if (t.type() == inout::LexemeType::Punctuation && t.lexeme() == u".") {
            // <AddressAtom> {["." <AddressAtom>] | "(" [<список параметров>] ")"}
            //                  ^
            ast().addNode_StepInto(SCRIPT_HOST_NAME, static_cast<ast::OperationCode>(BaseOperationCode::ObjectElement), t, t);

            t = getToken(tzer);

            if (!parseAddressAtom(tzer,t))
                return false;

            /// \todo Обрабатывать возврат false!
            ast().goParent();
        }
        else if (t.type() == inout::LexemeType::Punctuation && t.lexeme() == u"(") {
            // <AddressAtom> {["." <AddressAtom>] | "(" [<список параметров>] ")"}
            //                                       ^
            ast().addNode_StepInto(SCRIPT_HOST_NAME, static_cast<ast::OperationCode>(BaseOperationCode::FunctionCall), t, t);

            t = getToken(tzer);

            // <AddressAtom> {["." <AddressAtom>] | "(" [<список параметров>] ")"}
            //                                          ^
            if (t.type() != inout::LexemeType::Punctuation || t.lexeme() != u")") {
                // <AddressAtom> {["." <AddressAtom>] | "(" [<список параметров>] ")"}
                //                                           ^
                if (!parseArgumentList(tzer,t))
                    return false;

                // <AddressAtom> {["." <AddressAtom>] | "(" [<список параметров>] ")"}
                //                                                                 ^
                if (t.type() != inout::LexemeType::Punctuation || t.lexeme() != u")")
                {
                    reportUnexpected(t, "')'");
                    return false;
                }
            }

            /// \todo Обрабатывать возврат false!
            ast().goParent();

            t = getToken(tzer);
        }
        else
            break;
    }

    return true;
}

bool FuzeSblRdp::parseAddressAtom(inout::Tokenizer &tzer, inout::Token &t)
{
    // ID
    // ^
    if (t.type() != inout::LexemeType::Id) {
        reportUnexpected(t, inout::fmt("идентификатор"));
        return false;
    }

    ast().addNode(SCRIPT_HOST_NAME, static_cast<ast::OperationCode>(BaseOperationCode::PushVariable), t, t);

    t = getToken(tzer);

    return true;
}

bool FuzeSblRdp::parseArgumentList(inout::Tokenizer &tzer, inout::Token &t)
{
    while(t.type() != inout::LexemeType::Empty)
    {
        // { NUMBER | ANNOTATION | "true" | "false" | "null" | <адрес> [","] }
        //   ^        ^            ^        ^         ^        ^
        if (t.type() == inout::LexemeType::Number || t.type() == inout::LexemeType::Annotation)
        {
            ast().addNode(SCRIPT_HOST_NAME, static_cast<ast::OperationCode>(BaseOperationCode::PushConstant), t, t);

            t = getToken(tzer);
        }
        else if (t.type() == inout::LexemeType::Punctuation
             && t.qualification() == inout::TokenQualification::Keyword
             && (t.lexeme() == u"true" || t.lexeme() == u"false" || t.lexeme() == u"null"))
        {
            ast().addNode(SCRIPT_HOST_NAME, static_cast<ast::OperationCode>(BaseOperationCode::PushConstant), t, t);

            t = getToken(tzer);
        }
        else if (!parseAddress(tzer,t))
            return false;

        // { NUMBER | ANNOTATION | <адрес> [","] }
        //                                   ^
        if (t.type() != inout::LexemeType::Punctuation || t.lexeme() != u",")
            break;

        t = getToken(tzer);
    }

    return true;
}

inout::Token FuzeSblRdp::getToken(inout::Tokenizer & tzer) const
{
    inout::Token token = tzer.getAnyToken();

    while (token.type() == inout::LexemeType::Comment) {
        _syntax_data_collector.collectToken(token);

        token = tzer.getAnyToken();
    }

    _syntax_data_collector.collectToken(token);

    return token;
}

}